{------------------------------------------------------------------------------}
{                                                                              }
{  TSimpleGraph v1.542                                                         }
{  by Kambiz R. Khojasteh                                                      }
{                                                                              }
{  kambiz@delphiarea.com                                                       }
{  http://www.delphiarea.com                                                   }
{                                                                              }
{------------------------------------------------------------------------------}

{$Q-R-O+}

unit uSimpleGraph ;

interface

uses
   Windows,
   Messages,
   Classes,
   Graphics,
   Controls,
   Forms,
   Menus ;

const
   crHandFlat       = 51 ;
   crHandGrab       = 52 ;
   crHandPnt        = 53 ;
   crXHair1         = 54 ;
   crXHair2         = 55 ;

type

   { TGraphScrollBar -- for internal use only }

   TSimpleGraph = class ;

   TGraphScrollBar = class( TPersistent )
   private
      fOwner : TSimpleGraph ;
      fIncrement : TScrollBarInc ;
      fPageIncrement : TScrollbarInc ;
      fPosition : Integer ;
      fRange : Integer ;
      fCalcRange : Integer ;
      fKind : TScrollBarKind ;
      fMargin : Word ;
      fVisible : Boolean ;
      fTracking : Boolean ;
      fSmooth : Boolean ;
      fDelay : Integer ;
      fButtonSize : Integer ;
      fColor : TColor ;
      fParentColor : Boolean ;
      fSize : Integer ;
      fStyle : TScrollBarStyle ;
      fThumbSize : Integer ;
      fPageDiv : Integer ;
      fLineDiv : Integer ;
      fUpdateNeeded : Boolean ;
      constructor Create( AOwner : TSimpleGraph ; AKind : TScrollBarKind ) ;
      function ControlSize( ControlSB, AssumeSB : Boolean ) : Integer ;
      procedure DoSetRange( Value : Integer ) ;
      function GetScrollPos : Integer ;
      function NeedsScrollBarVisible : Boolean ;
      function IsIncrementStored : Boolean ;
      procedure ScrollMessage( var Msg : TWMScroll ) ;
      procedure SetButtonSize( Value : Integer ) ;
      procedure SetColor( Value : TColor ) ;
      procedure SetParentColor( Value : Boolean ) ;
      procedure SetPosition( Value : Integer ) ;
      procedure SetSize( Value : Integer ) ;
      procedure SetStyle( Value : TScrollBarStyle ) ;
      procedure SetThumbSize( Value : Integer ) ;
      procedure SetVisible( Value : Boolean ) ;
      procedure Update( ControlSB, AssumeSB : Boolean ) ;
   public
      procedure Assign( Source : TPersistent ) ; override ;
      procedure ChangeBiDiPosition ;
      property Kind : TScrollBarKind read FKind ;
      function IsScrollBarVisible : Boolean ;
      property ScrollPos : Integer read GetScrollPos ;
      property Range : Integer read fRange ;
      property Owner : TSimpleGraph read fOwner ;
   published
      property ButtonSize : Integer read fButtonSize write SetButtonSize default 0 ;
      property Color : TColor read fColor write SetColor default clBtnHighlight ;
      property Increment : TScrollBarInc read fIncrement write FIncrement stored IsIncrementStored default 8 ;
      property Margin : Word read fMargin write fMargin default 0 ;
      property ParentColor : Boolean read fParentColor write SetParentColor default True ;
      property Position : Integer read fPosition write SetPosition default 0 ;
      property Smooth : Boolean read fSmooth write FSmooth default False ;
      property Size : Integer read fSize write SetSize default 0 ;
      property Style : TScrollBarStyle read fStyle write SetStyle default ssRegular ;
      property ThumbSize : Integer read fThumbSize write SetThumbSize default 0 ;
      property Tracking : Boolean read fTracking write FTracking default False ;
      property Visible : Boolean read fVisible write SetVisible default True ;
   end ;

   { TSimpleGraph }

   TSimpleGraph = class( TCustomControl )
   private
      fZoom : integer ;
      fDefaultKeyMap : Boolean ;
      fModified : Boolean ;
      fHorzScrollBar : TGraphScrollBar ;
      fVertScrollBar : TGraphScrollBar ;
      fVisibleBounds : TRect ;
      fFreezeTopLeft : Boolean ;
      UpdatingScrollBars : Boolean ;
      UpdateCount : Integer ;
      GraphModified : Boolean ;
      Linking : Boolean ;
      IgnoreNotification : Boolean ;
      WheelAccumulator : Integer ;
      procedure SetHorzScrollBar( Value : TGraphScrollBar ) ;
      procedure SetVertScrollBar( Value : TGraphScrollBar ) ;
{$IFNDEF DELPHI5_UP}
      procedure WMContextMenu( var Message : TMessage ) ; message WM_CONTEXTMENU ;
{$ENDIF}
      procedure WMPaint( var Msg : TWMPaint ) ; message WM_PAINT ;
      procedure WMEraseBkgnd( var Msg : TWMEraseBkgnd ) ; message WM_ERASEBKGND ;
      procedure WMSize( var Msg : TWMSize ) ; message WM_SIZE ;
      procedure WMHScroll( var Msg : TWMHScroll ) ; message WM_HSCROLL ;
      procedure WMVScroll( var Msg : TWMVScroll ) ; message WM_VSCROLL ;
      procedure CMMouseLeave( var Msg : TMessage ) ; message CM_MOUSELEAVE ;
      //      procedure WMMouseWheel( var Message : TMessage ) ; message WM_MOUSEWHEEL ;
      procedure UpdateScrollBars ;
      procedure CalcVisibleBounds ;
   protected
      procedure CreateWnd ; override ;
      procedure Paint ; override ;
   public
      constructor Create( AOwner : TComponent ) ; override ;
      destructor Destroy ; override ;
      procedure BeginUpdate ;
      procedure EndUpdate ;
      procedure Invalidate ; override ;
      procedure ScrollInView( const Rect : TRect ) ; overload ;
      procedure ScrollInView( const Point : TPoint ) ; overload ;
      function ZoomRect( const Rect : TRect ) : Boolean ;
      function ClientToGraph( X, Y : Integer ) : TPoint ;
      function GraphToClient( X, Y : Integer ) : TPoint ;
   end ;

function IsBetween( Value : Integer ; Bound1, Bound2 : Integer ) : Boolean ;

function TransformRgn( Rgn : HRGN ; const XForm : TXForm ) : HRGN ;

procedure TransformPoints( var Points : array of TPoint ; const XForm : TXForm ) ;
procedure RotatePoints( var Points : array of TPoint ; const Angle : Extended ; const Org : TPoint ) ;
procedure OffsetPoints( var Points : array of TPoint ; dX, dY : Integer ) ;
function CenterOfPoints( const Points : array of TPoint ) : TPoint ;
function BoundsRectOfPoints( const Points : array of TPoint ) : TRect ;

function MakeRect( const Corner1, Corner2 : TPoint ) : TRect ;
function CenterOfRect( const Rect : TRect ) : TPoint ;

function LineSlopeAngle( const LinePt1, LinePt2 : TPoint ) : Extended ;
function DistanceToLine( const LinePt1, LinePt2, QueryPt : TPoint ) : Integer ;
function NextPointOfLine( const LineAngle : Extended ; const ThisPoint : TPoint ; Distance : Integer ) : TPoint ;

function IntersectLines( const Line1Pt : TPoint ;
   const Line1Angle : Extended ; const Line2Pt : TPoint ;
   const Line2Angle : Extended ; out Intersect : TPoint ) : Boolean ;

// In the following functions, the line passes through the center of shape
function IntersectLineRect( const LineAngle : Extended ;
   const Rect : TRect ; Backward : Boolean ) : TPoint ;
function IntersectLineEllipse( const LineAngle : Extended ;
   const Bounds : TRect ; Backward : Boolean ) : TPoint ;
function IntersectLineRoundRect( const LineAngle : Extended ;
   const Bounds : TRect ; Backward : Boolean ; Rgn : HRgn ) : TPoint ;
function IntersectLinePolygon( const LineAngle : Extended ;
   const Vertices : array of TPoint ; Backward : Boolean ) : TPoint ;

implementation

//{$R *.RES}

uses
   Math,
   SysUtils,
   CommCtrl,
   Clipbrd ;

{ Helper Functions }

function IsBetween( Value : Integer ; Bound1, Bound2 : Integer ) : Boolean ;
begin
   if Bound1 > Bound2 then begin
      Bound1 := Bound1 xor Bound2 ;
      Bound2 := Bound1 xor Bound2 ;
      Bound1 := Bound1 xor Bound2 ;
   end ;
   Result := ( Value >= Bound1 ) and ( Value <= Bound2 ) ;
end ;

procedure TransformPoints( var Points : array of TPoint ; const XForm : TXForm ) ;
var
   I                : Integer ;
begin
   for I := Low( Points ) to High( Points ) do
      with Points[ I ], XForm do begin
         X := Round( X * eM11 + Y * eM21 + eDx ) ;
         Y := Round( X * eM12 + Y * eM22 + eDy ) ;
      end ;
end ;

procedure RotatePoints( var Points : array of TPoint ;
   const Angle : Extended ; const Org : TPoint ) ;
var
   Sin, Cos         : Extended ;
   Prime            : TPoint ;
   I                : Integer ;
begin
   SinCos( Angle, Sin, Cos ) ;
   for I := Low( Points ) to High( Points ) do
      with Points[ I ] do begin
         Prime.X := X - Org.X ;
         Prime.Y := Y - Org.Y ;
         X := Round( Prime.X * Cos - Prime.Y * Sin ) + Org.X ;
         Y := Round( Prime.X * Sin + Prime.Y * Cos ) + Org.Y ;
      end ;
end ;

procedure OffsetPoints( var Points : array of TPoint ; dX, dY : Integer ) ;
var
   I                : Integer ;
begin
   for I := Low( Points ) to High( Points ) do
      with Points[ I ] do begin
         Inc( X, dX ) ;
         Inc( Y, dY ) ;
      end ;
end ;

function CenterOfPoints( const Points : array of TPoint ) : TPoint ;
var
   I                : Integer ;
   Sum              : TPoint ;
begin
   Sum.X := 0 ;
   Sum.Y := 0 ;
   for I := Low( Points ) to High( Points ) do
      with Points[ I ] do begin
         Inc( Sum.X, X ) ;
         Inc( Sum.Y, Y ) ;
      end ;
   Result.X := Sum.X div Length( Points ) ;
   Result.Y := Sum.Y div Length( Points ) ;
end ;

function BoundsRectOfPoints( const Points : array of TPoint ) : TRect ;
var
   I                : Integer ;
begin
   Result.TopLeft := Points[ Low( Points ) ] ;
   Result.BottomRight := Points[ Low( Points ) ] ;
   for I := Low( Points ) + 1 to High( Points ) do
      with Points[ I ], Result do begin
         if X < Left then
            Left := X ;
         if Y < Top then
            Top := Y ;
         if X > Right then
            Right := X ;
         if Y > Bottom then
            Bottom := Y ;
      end ;
end ;

function MakeRect( const Corner1, Corner2 : TPoint ) : TRect ;
begin
   if Corner1.X > Corner2.X then begin
      Result.Left := Corner2.X ;
      Result.Right := Corner1.X ;
   end
   else begin
      Result.Left := Corner1.X ;
      Result.Right := Corner2.X ;
   end ;
   if Corner1.Y > Corner2.Y then begin
      Result.Top := Corner2.Y ;
      Result.Bottom := Corner1.Y ;
   end
   else begin
      Result.Top := Corner1.Y ;
      Result.Bottom := Corner2.Y ;
   end
end ;

function CenterOfRect( const Rect : TRect ) : TPoint ;
begin
   Result.X := ( Rect.Left + Rect.Right ) div 2 ;
   Result.Y := ( Rect.Top + Rect.Bottom ) div 2 ;
end ;

function TransformRgn( Rgn : HRGN ; const XForm : TXForm ) : HRGN ;
var
   RgnData          : PRgnData ;
   RgnDataSize      : DWORD ;
begin
   Result := 0 ;
   RgnDataSize := GetRegionData( Rgn, 0, nil ) ;
   if RgnDataSize > 0 then begin
      GetMem( RgnData, RgnDataSize ) ;
      try
         GetRegionData( Rgn, RgnDataSize, RgnData ) ;
         Result := ExtCreateRegion( @Xform, RgnDataSize, RgnData^ ) ;
      finally
         FreeMem( RgnData ) ;
      end ;
   end ;
end ;

function LineSlopeAngle( const LinePt1, LinePt2 : TPoint ) : Extended ;
var
   dX, dY           : Integer ;
begin
   dX := LinePt2.X - LinePt1.X ;
   dY := LinePt2.Y - LinePt1.Y ;
   if dX <> 0 then
      Result := ArcTan2( dY, dX )
   else
      Result := Pi / 2 ;
end ;

function DistanceToLine( const LinePt1, LinePt2, QueryPt : TPoint ) : Integer ;
var
   M                : Extended ;
   Pt               : TPoint ;
begin
   if LinePt1.X = LinePt2.X then
      Result := Abs( QueryPt.X - LinePt1.X )
   else if LinePt1.Y = LinePt2.Y then
      Result := Abs( QueryPt.Y - LinePt1.Y )
   else begin
      M := ( LinePt2.Y - LinePt1.Y ) / ( LinePt2.X - LinePt1.X ) ;
      if ( M <> +1 ) and ( M <> -1 ) then begin
         Pt.X := Round( ( QueryPt.Y - LinePt1.Y + M * LinePt1.X - QueryPt.X / M ) / ( M - 1 / M ) ) ;
         Pt.Y := Round( LinePt1.Y + M * ( Pt.X - LinePt1.X ) ) ;
      end
      else begin
         Pt.Y := Round( ( M * ( QueryPt.X - LinePt1.X ) + ( QueryPt.Y + LinePt1.Y ) ) / 2 ) ;
         Pt.X := Round( LinePt1.X + ( Pt.Y - LinePt1.Y ) / M ) ;
      end ;
      Result := Round( Sqrt( Sqr( QueryPt.X - Pt.X ) + Sqr( QueryPt.Y - Pt.Y ) ) ) ;
   end ;
end ;

function NextPointOfLine( const LineAngle : Extended ; const ThisPoint : TPoint ;
   Distance : Integer ) : TPoint ;
var
   X, Y, M          : Extended ;
begin
   if Abs( LineAngle ) <> Pi / 2 then begin
      if Abs( LineAngle ) < Pi / 2 then
         Distance := -Distance ;
      M := Tan( LineAngle ) ;
      X := ThisPoint.X + Distance / Sqrt( 1 + Sqr( M ) ) ;
      Y := ThisPoint.Y + M * ( X - ThisPoint.X ) ;
      Result := Point( Round( X ), Round( Y ) ) ;
   end
   else begin
      if LineAngle > 0 then
         Distance := -Distance ;
      Result := Point( ThisPoint.X, ThisPoint.Y + Distance ) ;
   end ;
end ;

function IntersectLines( const Line1Pt : TPoint ; const Line1Angle : Extended ;
   const Line2Pt : TPoint ; const Line2Angle : Extended ; out Intersect : TPoint ) : Boolean ;
var
   M1, M2           : Extended ;
   C1, C2           : Extended ;
begin
   Result := True ;
   if ( Line1Angle = Line2Angle ) or
      ( ( Abs( Line1Angle ) = Pi / 2 ) and ( Abs( Line2Angle ) = Pi / 2 ) ) then // Lines have identical slope, so they are either parallel or identical
      Result := False
   else if Abs( Line1Angle ) = Pi / 2 then begin
      M2 := Tan( Line2Angle ) ;
      C2 := Line2Pt.Y - M2 * Line2Pt.X ;
      Intersect.X := Line1Pt.X ;
      Intersect.Y := Round( M2 * Line2Pt.X + C2 ) ;
   end
   else if Abs( Line2Angle ) = Pi / 2 then begin
      M1 := Tan( Line1Angle ) ;
      C1 := Line1Pt.Y - M1 * Line1Pt.X ;
      Intersect.X := Line2Pt.X ;
      Intersect.Y := Round( M1 * Line1Pt.X + C1 ) ;
   end
   else begin
      M1 := Tan( Line1Angle ) ;
      C1 := Line1Pt.Y - M1 * Line1Pt.X ;
      M2 := Tan( Line2Angle ) ;
      C2 := Line2Pt.Y - M2 * Line2Pt.X ;
      Intersect.X := Round( ( C1 - C2 ) / ( M2 - M1 ) ) ;
      Intersect.Y := Round( ( M2 * C1 - M1 * C2 ) / ( M2 - M1 ) ) ;
   end ;
end ;

function IntersectLineRect( const LineAngle : Extended ;
   const Rect : TRect ; Backward : Boolean ) : TPoint ;
var
   M, C, A          : Extended ;
   Xc, Yc           : Extended ;
begin
   Xc := ( Rect.Left + Rect.Right ) / 2 ;
   Yc := ( Rect.Top + Rect.Bottom ) / 2 ;
   if Abs( LineAngle ) = Pi / 2 then begin
      if ( LineAngle > 0 ) xor Backward then
         Result := Point( Round( Xc ), Rect.Bottom )
      else
         Result := Point( Round( Xc ), Rect.Top ) ;
   end
   else if ( LineAngle = 0 ) or ( Abs( LineAngle ) = Pi ) then begin
      if ( LineAngle <> 0 ) xor Backward then
         Result := Point( Rect.Left, Round( Yc ) )
      else
         Result := Point( Rect.Right, Round( Yc ) ) ;
   end
   else begin
      M := Tan( LineAngle ) ;
      C := Yc - M * Xc ;
      A := 0 ;
      if ( Rect.Right - Rect.Left ) > 0 then
         A := ArcTan2( ( Rect.Bottom - Rect.Top ) / 2, ( Rect.Right - Rect.Left ) / 2 ) ;
      if ( ( Abs( LineAngle ) >= 0 ) and ( Abs( LineAngle ) <= A ) and Backward ) or
         ( ( Pi - Abs( LineAngle ) >= 0 ) and ( Pi - Abs( LineAngle ) <= A ) and not Backward ) then
         Result := Point( Rect.Left, Round( M * Rect.Left + C ) )
      else if ( ( Abs( LineAngle ) >= 0 ) and ( Abs( LineAngle ) <= A ) and not Backward ) or
         ( ( Pi - Abs( LineAngle ) >= 0 ) and ( Pi - Abs( LineAngle ) <= A ) and Backward ) then
         Result := Point( Rect.Right, Round( M * Rect.Right + C ) )
      else if ( LineAngle > 0 ) xor Backward then
         Result := Point( Round( ( Rect.Bottom - C ) / M ), Rect.Bottom )
      else
         Result := Point( Round( ( Rect.Top - C ) / M ), Rect.Top ) ;
   end ;
end ;

function IntersectLineEllipse( const LineAngle : Extended ;
   const Bounds : TRect ; Backward : Boolean ) : TPoint ;
var
   A2, B2, M, T     : Extended ;
   Xc, Yc, X, Y     : Extended ;
begin
   Xc := ( Bounds.Left + Bounds.Right ) / 2 ;
   Yc := ( Bounds.Top + Bounds.Bottom ) / 2 ;
   if Abs( LineAngle ) = Pi / 2 then begin
      if ( LineAngle > 0 ) xor Backward then
         Result := Point( Round( Xc ), Bounds.Bottom )
      else
         Result := Point( Round( Xc ), Bounds.Top ) ;
   end
   else if ( LineAngle = 0 ) or ( Abs( LineAngle ) = Pi ) then begin
      if ( LineAngle <> 0 ) xor Backward then
         Result := Point( Bounds.Left, Round( Yc ) )
      else
         Result := Point( Bounds.Right, Round( Yc ) ) ;
   end
   else begin
      M := Tan( LineAngle ) ;
      A2 := Sqr( ( Bounds.Right - Bounds.Left ) / 2 ) ;
      B2 := Sqr( ( Bounds.Bottom - Bounds.Top ) / 2 ) ;
      T := B2 + A2 * Sqr( M ) ;
      if ( Abs( LineAngle ) < Pi / 2 ) xor Backward then
         X := Sqrt( T * ( A2 * B2 ) ) / T
      else
         X := -Sqrt( T * ( A2 * B2 ) ) / T ;
      Y := M * X ;
      Result := Point( Round( X + Xc ), Round( Y + Yc ) ) ;
   end ;
end ;

function IntersectLineRoundRect( const LineAngle : Extended ;
   const Bounds : TRect ; Backward : Boolean ; Rgn : HRgn ) : TPoint ;
var
   CR               : TRect ;
   Sw, Sh, W, H     : Integer ;
   A2, B2, M, C     : Extended ;
   Xc, Yc, X, Y     : Extended ;
   a, b, d          : Extended ;
begin
   Result := IntersectLineRect( LineAngle, Bounds, Backward ) ;
   SetRect( CR, Result.X, Result.Y, Result.X, Result.Y ) ;
   InflateRect( CR, 1, 1 ) ;
   if not RectInRegion( Rgn, CR ) and ( Abs( LineAngle ) <> Pi / 2 ) then begin
      W := Bounds.Right - Bounds.Left ;
      H := Bounds.Bottom - Bounds.Top ;
      if W > H then begin
         Sw := W div 4 ;
         if Sw > H then
            Sh := H
         else
            Sh := Sw ;
      end
      else begin
         Sh := H div 4 ;
         if Sh > W then
            Sw := W
         else
            Sw := Sh ;
      end ;
      if ( ( LineAngle > 0 ) and ( LineAngle < Pi / 2 ) and Backward ) or
         ( ( LineAngle < -Pi / 2 ) and ( LineAngle > -Pi ) and not Backward ) then
         SetRect( CR, Bounds.Left, Bounds.Top, Bounds.Left + Sw, Bounds.Top + Sh )
      else if ( ( LineAngle > 0 ) and ( LineAngle < Pi / 2 ) and not Backward ) or
         ( ( LineAngle < -Pi / 2 ) and ( LineAngle > -Pi ) and Backward ) then
         SetRect( CR, Bounds.Right - Sw, Bounds.Bottom - Sh, Bounds.Right, Bounds.Bottom )
      else if ( ( LineAngle < 0 ) and ( LineAngle > -Pi / 2 ) and Backward ) or
         ( ( LineAngle > Pi / 2 ) and ( LineAngle < Pi ) and not Backward ) then
         SetRect( CR, Bounds.Left, Bounds.Bottom - Sh, Bounds.Left + Sw, Bounds.Bottom )
      else if ( ( LineAngle < 0 ) and ( LineAngle > -Pi / 2 ) and not Backward ) or
         ( ( LineAngle > Pi / 2 ) and ( LineAngle < Pi ) and Backward ) then
         SetRect( CR, Bounds.Right - Sw, Bounds.Top, Bounds.Right, Bounds.Top + Sh ) ;
      Xc := ( Bounds.Left + Bounds.Right ) / 2 ;
      Yc := ( Bounds.Top + Bounds.Bottom ) / 2 ;
      M := Tan( LineAngle ) ;
      C := Yc - M * Xc ;
      Xc := ( CR.Left + CR.Right ) / 2 ;
      Yc := ( CR.Top + CR.Bottom ) / 2 ;
      A2 := Sqr( Sw / 2 ) ;
      B2 := Sqr( Sh / 2 ) ;
      a := ( B2 + A2 * Sqr( M ) ) ;
      b := ( A2 * M * ( C - Yc ) ) - B2 * Xc ;
      d := Sqr( b ) - a * ( B2 * Sqr( Xc ) + A2 * Sqr( C - Yc ) - A2 * B2 ) ;
      if d > 0 then begin
         if ( Abs( LineAngle ) < Pi / 2 ) xor Backward then
            X := ( -b + Sqrt( d ) ) / a
         else
            X := ( -b - Sqrt( Sqr( b ) - a * ( B2 * Sqr( Xc ) + A2 * Sqr( C - Yc ) - A2 * B2 ) ) ) / a ;
         Y := M * X + C ;
         Result := Point( Round( X ), Round( Y ) ) ;
      end ;
   end ;
end ;

{ NOTE: Vertices are in clockwise order, and the first vertex is at 12 O'clock }

function IntersectLinePolygon( const LineAngle : Extended ;
   const Vertices : array of TPoint ; Backward : Boolean ) : TPoint ;

   function IntersectEdge( const Center : TPoint ;
      V1, V2 : Integer ; out Intersect : TPoint ) : Boolean ;
   var
      EdgeAngle     : Extended ;
   begin
      EdgeAngle := LineSlopeAngle( Vertices[ V1 ], Vertices[ V2 ] ) ;
      Result := IntersectLines( Center, LineAngle, Vertices[ V1 ], EdgeAngle, Intersect )
         and IsBetween( Intersect.X, Vertices[ V1 ].X, Vertices[ V2 ].X )
         and IsBetween( Intersect.Y, Vertices[ V1 ].Y, Vertices[ V2 ].Y ) ;
   end ;

var
   I                : Integer ;
   Center           : TPoint ;
begin
   Center := CenterOfPoints( Vertices ) ;
   if not Backward xor ( ( LineAngle >= -Pi / 2 ) and ( LineAngle < Pi / 2 ) ) then begin
      if IntersectEdge( Center, Low( Vertices ), High( Vertices ), Result ) and
         ( ( Result.X <> Vertices[ Low( Vertices ) ].X ) or ( Result.Y <> Vertices[ Low( Vertices ) ].Y ) ) then
         Exit ;
      for I := High( Vertices ) downto Low( Vertices ) + 1 do
         if IntersectEdge( Center, I, I - 1, Result ) then
            Exit ;
   end
   else begin
      for I := Low( Vertices ) to High( Vertices ) - 1 do
         if IntersectEdge( Center, I, I + 1, Result ) then
            Exit ;
      if IntersectEdge( Center, High( Vertices ), Low( Vertices ), Result ) then
         Exit ;
   end ;
   Result := Center ;
end ;

{ TGraphScrollBar }

constructor TGraphScrollBar.Create( AOwner : TSimpleGraph ; AKind : TScrollBarKind ) ;
begin
   inherited Create ;
   fOwner := AOwner ;
   fKind := AKind ;
   fPageIncrement := 80 ;
   fIncrement := fPageIncrement div 10 ;
   fVisible := True ;
   fDelay := 10 ;
   fLineDiv := 4 ;
   fPageDiv := 12 ;
   fColor := clBtnHighlight ;
   fParentColor := True ;
   fUpdateNeeded := True ;
   fStyle := ssRegular ;
end ;

function TGraphScrollBar.IsIncrementStored : Boolean ;
begin
   Result := not Smooth ;
end ;

procedure TGraphScrollBar.Assign( Source : TPersistent ) ;
begin
   if Source is TGraphScrollBar then begin
      Visible := TGraphScrollBar( Source ).Visible ;
      Position := TGraphScrollBar( Source ).Position ;
      Increment := TGraphScrollBar( Source ).Increment ;
      DoSetRange( TGraphScrollBar( Source ).Range ) ;
   end
   else
      inherited Assign( Source ) ;
end ;

procedure TGraphScrollBar.ChangeBiDiPosition ;
begin
   if Kind = sbHorizontal then
      if IsScrollBarVisible then
         if not Owner.UseRightToLeftScrollBar then
            Position := 0
         else
            Position := Range ;
end ;

function TGraphScrollBar.IsScrollBarVisible : Boolean ;
var
   Style            : Longint ;
begin
   Style := WS_HSCROLL ;
   if Kind = sbVertical then
      Style := WS_VSCROLL ;
   Result := ( Visible ) and
      ( GetWindowLong( Owner.Handle, GWL_STYLE ) and Style <> 0 ) ;
end ;

function TGraphScrollBar.ControlSize( ControlSB, AssumeSB : Boolean ) : Integer ;
var
   BorderAdjust     : Integer ;

   function ScrollBarVisible( Code : Word ) : Boolean ;
   var
      Style         : Longint ;
   begin
      Style := WS_HSCROLL ;
      if Code = SB_VERT then
         Style := WS_VSCROLL ;
      Result := GetWindowLong( Owner.Handle, GWL_STYLE ) and Style <> 0 ;
   end ;

   function Adjustment( Code, Metric : Word ) : Integer ;
   begin
      Result := 0 ;
      if not ControlSB then
         if AssumeSB and not ScrollBarVisible( Code ) then
            Result := -( GetSystemMetrics( Metric ) - BorderAdjust )
         else if not AssumeSB and ScrollBarVisible( Code ) then
            Result := GetSystemMetrics( Metric ) - BorderAdjust ;
   end ;

begin
   BorderAdjust := Integer( GetWindowLong( Owner.Handle, GWL_STYLE ) and
      ( WS_BORDER or WS_THICKFRAME ) <> 0 ) ;
   if Kind = sbVertical then
      Result := Owner.ClientHeight + Adjustment( SB_HORZ, SM_CXHSCROLL )
   else
      Result := Owner.ClientWidth + Adjustment( SB_VERT, SM_CYVSCROLL ) ;
end ;

function TGraphScrollBar.GetScrollPos : Integer ;
begin
   Result := 0 ;
   if Visible then
      Result := Position ;
end ;

function TGraphScrollBar.NeedsScrollBarVisible : Boolean ;
begin
   Result := fRange > ControlSize( False, False ) ;
end ;

procedure TGraphScrollBar.ScrollMessage( var Msg : TWMScroll ) ;
var
   Incr, FinalIncr, Count : Integer ;
   CurrentTime, StartTime, ElapsedTime : Longint ;

   function GetRealScrollPosition : Integer ;
   var
      SI            : TScrollInfo ;
      Code          : Integer ;
   begin
      SI.cbSize := SizeOf( TScrollInfo ) ;
      SI.fMask := SIF_TRACKPOS ;
      Code := SB_HORZ ;
      if fKind = sbVertical then
         Code := SB_VERT ;
      Result := Msg.Pos ;
      if FlatSB_GetScrollInfo( Owner.Handle, Code, SI ) then
         Result := SI.nTrackPos ;
   end ;

begin
   with Msg do begin
      if fSmooth and ( ScrollCode in [ SB_LINEUP, SB_LINEDOWN, SB_PAGEUP, SB_PAGEDOWN ] ) then begin
         case ScrollCode of
            SB_LINEUP, SB_LINEDOWN : begin
                  Incr := fIncrement div fLineDiv ;
                  FinalIncr := fIncrement mod fLineDiv ;
                  Count := fLineDiv ;
               end ;
            SB_PAGEUP, SB_PAGEDOWN : begin
                  Incr := FPageIncrement ;
                  FinalIncr := Incr mod fPageDiv ;
                  Incr := Incr div fPageDiv ;
                  Count := fPageDiv ;
               end ;
         else
            Count := 0 ;
            Incr := 0 ;
            FinalIncr := 0 ;
         end ;
         CurrentTime := 0 ;
         while Count > 0 do begin
            StartTime := GetTickCount ;
            ElapsedTime := StartTime - CurrentTime ;
            if ElapsedTime < fDelay then
               Sleep( fDelay - ElapsedTime ) ;
            CurrentTime := StartTime ;
            case ScrollCode of
               SB_LINEUP : SetPosition( fPosition - Incr ) ;
               SB_LINEDOWN : SetPosition( fPosition + Incr ) ;
               SB_PAGEUP : SetPosition( fPosition - Incr ) ;
               SB_PAGEDOWN : SetPosition( fPosition + Incr ) ;
            end ;
            Owner.Update ;
            Dec( Count ) ;
         end ;
         if FinalIncr > 0 then begin
            case ScrollCode of
               SB_LINEUP : SetPosition( fPosition - FinalIncr ) ;
               SB_LINEDOWN : SetPosition( fPosition + FinalIncr ) ;
               SB_PAGEUP : SetPosition( fPosition - FinalIncr ) ;
               SB_PAGEDOWN : SetPosition( fPosition + FinalIncr ) ;
            end ;
         end ;
      end
      else
         case ScrollCode of
            SB_LINEUP : SetPosition( fPosition - fIncrement ) ;
            SB_LINEDOWN : SetPosition( fPosition + fIncrement ) ;
            SB_PAGEUP : SetPosition( fPosition - ControlSize( True, False ) ) ;
            SB_PAGEDOWN : SetPosition( fPosition + ControlSize( True, False ) ) ;
            SB_THUMBPOSITION :
               if fCalcRange > 32767 then
                  SetPosition( GetRealScrollPosition )
               else
                  SetPosition( Pos ) ;
            SB_THUMBTRACK :
               if Tracking then
                  if fCalcRange > 32767 then
                     SetPosition( GetRealScrollPosition )
                  else
                     SetPosition( Pos ) ;
            SB_TOP : SetPosition( 0 ) ;
            SB_BOTTOM : SetPosition( fCalcRange ) ;
            SB_ENDSCROLL : begin
               end ;
         end ;
   end ;
end ;

procedure TGraphScrollBar.SetButtonSize( Value : Integer ) ;
const
   SysConsts        : array[ TScrollBarKind ] of Integer = ( SM_CXHSCROLL, SM_CXVSCROLL ) ;
var
   NewValue         : Integer ;
begin
   if Value <> ButtonSize then begin
      NewValue := Value ;
      if NewValue = 0 then
         Value := GetSystemMetrics( SysConsts[ Kind ] ) ;
      fButtonSize := Value ;
      fUpdateNeeded := True ;
      Owner.UpdateScrollBars ;
      if NewValue = 0 then
         fButtonSize := 0 ;
   end ;
end ;

procedure TGraphScrollBar.SetColor( Value : TColor ) ;
begin
   if Value <> Color then begin
      fColor := Value ;
      fParentColor := False ;
      fUpdateNeeded := True ;
      Owner.UpdateScrollBars ;
   end ;
end ;

procedure TGraphScrollBar.SetParentColor( Value : Boolean ) ;
begin
   if ParentColor <> Value then begin
      fParentColor := Value ;
      if Value then
         Color := clBtnHighlight ;
   end ;
end ;

procedure TGraphScrollBar.SetPosition( Value : Integer ) ;
var
   Code             : Word ;
   Form             : TCustomForm ;
   OldPos           : Integer ;
begin
   if csReading in Owner.ComponentState then
      fPosition := Value
   else begin
      if Value > fCalcRange then
         Value := fCalcRange
      else if Value < 0 then
         Value := 0 ;
      if Kind = sbHorizontal then
         Code := SB_HORZ
      else
         Code := SB_VERT ;
      if Value <> FPosition then begin
         OldPos := FPosition ;
         fPosition := Value ;
         if Kind = sbHorizontal then
            Owner.ScrollBy( OldPos - Value, 0 )
         else
            Owner.ScrollBy( 0, OldPos - Value ) ;
         if csDesigning in Owner.ComponentState then begin
            Form := GetParentForm( Owner ) ;
            if ( Form <> nil ) and ( Form.Designer <> nil ) then
               Form.Designer.Modified ;
         end ;
      end ;
      if FlatSB_GetScrollPos( Owner.Handle, Code ) <> fPosition then
         FlatSB_SetScrollPos( Owner.Handle, Code, fPosition, True ) ;
      Owner.CalcVisibleBounds ;
   end ;
end ;

procedure TGraphScrollBar.SetSize( Value : Integer ) ;
const
   SysConsts        : array[ TScrollBarKind ] of Integer = ( SM_CYHSCROLL, SM_CYVSCROLL ) ;
var
   NewValue         : Integer ;
begin
   if Value <> Size then begin
      NewValue := Value ;
      if NewValue = 0 then
         Value := GetSystemMetrics( SysConsts[ Kind ] ) ;
      fSize := Value ;
      fUpdateNeeded := True ;
      Owner.UpdateScrollBars ;
      if NewValue = 0 then
         fSize := 0 ;
   end ;
end ;

procedure TGraphScrollBar.SetStyle( Value : TScrollBarStyle ) ;
begin
   if Style <> Value then begin
      fStyle := Value ;
      fUpdateNeeded := True ;
      Owner.UpdateScrollBars ;
   end ;
end ;

procedure TGraphScrollBar.SetThumbSize( Value : Integer ) ;
begin
   if ThumbSize <> Value then begin
      fThumbSize := Value ;
      fUpdateNeeded := True ;
      Owner.UpdateScrollBars ;
   end ;
end ;

procedure TGraphScrollBar.DoSetRange( Value : Integer ) ;
var
   NewRange         : Integer ;
begin
   if Value <= 0 then
      NewRange := 0
   else
      NewRange := MulDiv( Value, Owner.fZoom, 100 ) ;
   if fRange <> NewRange then begin
      fRange := NewRange ;
      Owner.UpdateScrollBars ;
   end ;
end ;

procedure TGraphScrollBar.SetVisible( Value : Boolean ) ;
begin
   if fVisible <> Value then begin
      fVisible := Value ;
      Owner.UpdateScrollBars ;
   end ;
end ;

procedure TGraphScrollBar.Update( ControlSB, AssumeSB : Boolean ) ;
type
   TPropKind = ( pkStyle, pkButtonSize, pkThumbSize, pkSize, pkBkColor ) ;
const
   Kinds            : array[ TScrollBarKind ] of Integer = ( WSB_PROP_HSTYLE, WSB_PROP_VSTYLE ) ;
   Styles           : array[ TScrollBarStyle ] of Integer = ( FSB_REGULAR_MODE,
      FSB_ENCARTA_MODE, FSB_FLAT_MODE ) ;
   Props            : array[ TScrollBarKind, TPropKind ] of Integer = (
      { Horizontal }
      ( WSB_PROP_HSTYLE, WSB_PROP_CXHSCROLL, WSB_PROP_CXHTHUMB, WSB_PROP_CYHSCROLL,
      WSB_PROP_HBKGCOLOR ),
      { Vertical }
      ( WSB_PROP_VSTYLE, WSB_PROP_CYVSCROLL, WSB_PROP_CYVTHUMB, WSB_PROP_CXVSCROLL,
      WSB_PROP_VBKGCOLOR ) ) ;
var
   Code             : Word ;
   ScrollInfo       : TScrollInfo ;

   procedure UpdateScrollProperties( Redraw : Boolean ) ;
   begin
      FlatSB_SetScrollProp( Owner.Handle, Props[ Kind, pkStyle ], Styles[ Style ], Redraw ) ;
      if ButtonSize > 0 then
         FlatSB_SetScrollProp( Owner.Handle, Props[ Kind, pkButtonSize ], ButtonSize, False ) ;
      if ThumbSize > 0 then
         FlatSB_SetScrollProp( Owner.Handle, Props[ Kind, pkThumbSize ], ThumbSize, False ) ;
      if Size > 0 then
         FlatSB_SetScrollProp( Owner.Handle, Props[ Kind, pkSize ], Size, False ) ;
      FlatSB_SetScrollProp( Owner.Handle, Props[ Kind, pkBkColor ],
         ColorToRGB( Color ), False ) ;
   end ;

begin
   fCalcRange := 0 ;
   Code := SB_HORZ ;
   if Kind = sbVertical then
      Code := SB_VERT ;
   if Visible then begin
      fCalcRange := Range - ControlSize( ControlSB, AssumeSB ) ;
      if fCalcRange < 0 then
         fCalcRange := 0 ;
   end ;
   ScrollInfo.cbSize := SizeOf( ScrollInfo ) ;
   ScrollInfo.fMask := SIF_ALL ;
   ScrollInfo.nMin := 0 ;
   if fCalcRange > 0 then
      ScrollInfo.nMax := Range
   else
      ScrollInfo.nMax := 0 ;
   ScrollInfo.nPage := ControlSize( ControlSB, AssumeSB ) + 1 ;
   ScrollInfo.nPos := fPosition ;
   ScrollInfo.nTrackPos := fPosition ;
   UpdateScrollProperties( fUpdateNeeded ) ;
   fUpdateNeeded := False ;
   FlatSB_SetScrollInfo( Owner.Handle, Code, ScrollInfo, True ) ;
   SetPosition( fPosition ) ;
   //   fPageIncrement := ( ControlSize( True, False ) * 9 ) div 10 ;
   if Smooth then
      fIncrement := fPageIncrement div 10 ;
end ;

{ TSimpleGraph }

constructor TSimpleGraph.Create( AOwner : TComponent ) ;
begin
   inherited Create( AOwner ) ;
   fFreezeTopLeft := False ;
   fDefaultKeyMap := True ;
   fModified := False ;
   fHorzScrollBar := TGraphScrollBar.Create( Self, sbHorizontal ) ;
   fVertScrollBar := TGraphScrollBar.Create( Self, sbVertical ) ;
end ;

destructor TSimpleGraph.Destroy ;
begin
   fHorzScrollBar.Free ;
   fVertScrollBar.Free ;
   inherited Destroy ;
end ;

{$IFNDEF DELPHI5_UP}

procedure TSimpleGraph.WMContextMenu( var Message : TMessage ) ;
var
   Handled          : Boolean ;
   MousePos         : TPoint ;
begin
   Handled := False ;
   MousePos.X := LoWord( Message.LParam ) ;
   MousePos.Y := HiWord( Message.LParam ) ;
   MousePos := ScreenToClient( MousePos ) ;
   DoContextPopup( MousePos, Handled ) ;
   if Handled then
      Message.Result := 1
   else
      inherited ;
end ;
{$ENDIF}

procedure TSimpleGraph.WMPaint(var Msg: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
  SavedDC: Integer;
  Offset: Integer;
begin
  if Msg.DC <> 0 then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited
    else
      PaintHandler(Msg);
  end
  else
  begin
    Offset := fZoom div 100;
    DC := GetDC(0);
    try
      with ClientRect do
        MemBitmap := CreateCompatibleBitmap(DC,
          Right + fHorzScrollBar.Position + Offset,
          Bottom + fVertScrollBar.Position + Offset);
    finally
      ReleaseDC(0, DC);
    end;
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
    try
      SavedDC := SaveDC(MemDC);
      try
        SetMapMode(MemDC, MM_ANISOTROPIC);
        SetWindowExtEx(MemDC, 100, 100, nil);
        SetViewPortExtEx(MemDC, fZoom,fZoom, nil);
        Msg.DC := MemDC;
        try
          WMPaint(Msg);
        finally
          Msg.DC := 0;
        end;
      finally
        RestoreDC(MemDC, SavedDC);
      end;
      DC := BeginPaint(WindowHandle, PS);
      try
        BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC,
          fHorzScrollBar.Position, fVertScrollBar.Position, SRCCOPY);
      finally
        EndPaint(WindowHandle, PS);
      end;
    finally
      SelectObject(MemDC, OldBitmap);
      DeleteDC(MemDC);
      DeleteObject(MemBitmap);
    end;
  end;
end;

procedure TSimpleGraph.WMEraseBkgnd( var Msg : TWMEraseBkgnd ) ;
begin
   Msg.Result := 1 ;
end ;

procedure TSimpleGraph.WMSize( var Msg : TWMSize ) ;
begin
   UpdatingScrollBars := True ;
   try
      //      CalcAutoRange ;
   finally
      UpdatingScrollBars := False ;
   end ;
   if fHorzScrollBar.Visible or fVertScrollBar.Visible then
      UpdateScrollBars
   else
      CalcVisibleBounds ;
end ;

procedure TSimpleGraph.WMHScroll( var Msg : TWMHScroll ) ;
begin
   if ( Msg.ScrollBar = 0 ) and fHorzScrollBar.Visible then
      fHorzScrollBar.ScrollMessage( Msg )
   else
      inherited ;
end ;

procedure TSimpleGraph.WMVScroll( var Msg : TWMVScroll ) ;
begin
   if ( Msg.ScrollBar = 0 ) and fVertScrollBar.Visible then
      fVertScrollBar.ScrollMessage( Msg )
   else
      inherited ;
end ;

procedure TSimpleGraph.CMMouseLeave( var Msg : TMessage ) ;
begin
   inherited ;
   if ( GetCapture <> WindowHandle ) then
      Screen.Cursor := crDefault ;
end ;

{procedure TSimpleGraph.WMMouseWheel( var Message : TMessage ) ;
var
   IsNeg            : Boolean ;
   Rect             : TRect ;
   Pt               : TPoint ;
begin
   GetWindowRect( WindowHandle, Rect ) ;
   Pt.X := LoWord( Message.LParam ) ;
   Pt.Y := HiWord( Message.LParam ) ;
   if PtInRect( Rect, Pt ) then begin
      Message.Result := 1 ;
      Inc( WheelAccumulator, SmallInt( HiWord( Message.WParam ) ) ) ;
      while Abs( WheelAccumulator ) >= WHEEL_DELTA do begin
         IsNeg := WheelAccumulator < 0 ;
         WheelAccumulator := Abs( WheelAccumulator ) - WHEEL_DELTA ;
         if IsNeg then begin
            WheelAccumulator := -WheelAccumulator ;
            if Zoom > ZoomStep then
               Zoom := Zoom - ZoomStep
            else
               Zoom := ZoomMin ;
         end
         else
            Zoom := Zoom + ZoomStep ;
      end ;
   end ;
end ;}

procedure TSimpleGraph.CreateWnd ;
begin
   inherited CreateWnd ;
   if not SysLocale.MiddleEast then
      InitializeFlatSB( WindowHandle ) ;
   UpdateScrollBars ;
end ;

{procedure TSimpleGraph.DoEnter ;
begin
   inherited DoEnter ;
   if HideSelection then
      Invalidate ;
end ;

procedure TSimpleGraph.DoExit ;
begin
   inherited DoExit ;
   if HideSelection then
      Invalidate ;
end ; }

procedure TSimpleGraph.ScrollInView( const Rect : TRect ) ;
begin
   ScrollInView( Rect.BottomRight ) ;
   ScrollInView( Rect.TopLeft ) ;
end ;

procedure TSimpleGraph.ScrollInView( const Point : TPoint ) ;
var
   X, Y             : Integer ;
begin
   X := MulDiv( Point.X, fZoom, 100 ) ;
   Y := MulDiv( Point.Y, fZoom, 100 ) ;
   with fHorzScrollBar do
      if IsScrollBarVisible then begin
         if X < Position then
            Position := X
         else if X > Position + ClientWidth then
            Position := X - ClientWidth ;
      end ;
   with fVertScrollBar do
      if IsScrollBarVisible then begin
         if Y < Position then
            Position := Y
         else if Y > Position + ClientHeight then
            Position := Y - ClientHeight ;
      end ;
end ;

procedure TSimpleGraph.BeginUpdate ;
begin
   Inc( UpdateCount ) ;
end ;

procedure TSimpleGraph.EndUpdate ;
begin
   Dec( UpdateCount ) ;
end ;

procedure TSimpleGraph.Invalidate ;
begin
   if ( WindowHandle <> 0 ) and ( UpdateCount = 0 ) then
      InvalidateRect( WindowHandle, nil, False ) ;
end ;

procedure TSimpleGraph.SetHorzScrollBar( Value : TGraphScrollBar ) ;
begin
   fHorzScrollBar.Assign( Value ) ;
end ;

procedure TSimpleGraph.SetVertScrollBar( Value : TGraphScrollBar ) ;
begin
   fVertScrollBar.Assign( Value ) ;
end ;

procedure TSimpleGraph.UpdateScrollBars ;
begin
   if not UpdatingScrollBars and HandleAllocated then begin
      try
         UpdatingScrollBars := True ;
         if fVertScrollBar.NeedsScrollBarVisible then begin
            fHorzScrollBar.Update( False, True ) ;
            fVertScrollBar.Update( True, False ) ;
         end
         else if fHorzScrollBar.NeedsScrollBarVisible then begin
            fVertScrollBar.Update( False, True ) ;
            fHorzScrollBar.Update( True, False ) ;
         end
         else begin
            fVertScrollBar.Update( False, False ) ;
            fHorzScrollBar.Update( True, False ) ;
         end ;
      finally
         UpdatingScrollBars := False ;
      end ;
      CalcVisibleBounds ;
   end ;
end ;

procedure TSimpleGraph.CalcVisibleBounds ;
begin
   with ClientRect do begin
      fVisibleBounds.TopLeft := ClientToGraph( Left, Top ) ;
      fVisibleBounds.BottomRight := ClientToGraph( Right, Bottom ) ;
   end ;
end ;

function TSimpleGraph.ClientToGraph( X, Y : Integer ) : TPoint ;
begin
   Result.X := MulDiv( X + fHorzScrollBar.Position, 100, fZoom ) ;
   Result.Y := MulDiv( Y + fVertScrollBar.Position, 100, fZoom ) ;
end ;

function TSimpleGraph.GraphToClient( X, Y : Integer ) : TPoint ;
begin
   Result.X := MulDiv( X, fZoom, 100 ) - fHorzScrollBar.Position ;
   Result.Y := MulDiv( Y, fZoom, 100 ) - fVertScrollBar.Position ;
end ;

function TSimpleGraph.ZoomRect( const Rect : TRect ) : Boolean ;
var
   HZoom, VZoom     : Integer ;
   CRect            : TRect ;
begin
   CRect := ClientRect ;
   if not fVertScrollBar.IsScrollBarVisible then
      Dec( CRect.Right, GetSystemMetrics( SM_CXVSCROLL ) ) ;
   if not fHorzScrollBar.IsScrollBarVisible then
      Dec( CRect.Bottom, GetSystemMetrics( SM_CYHSCROLL ) ) ;
   HZoom := MulDiv( 100, CRect.Right - CRect.Left, Rect.Right - Rect.Left ) ;
   VZoom := MulDiv( 100, CRect.Bottom - CRect.Top, Rect.Bottom - Rect.Top ) ;
   ScrollInView( Rect ) ;
   Result := ( fZoom = HZoom ) or ( fZoom = VZoom ) ;
end ;

procedure TSimpleGraph.Paint ;
var
   Rect             : TRect ;
begin
   Canvas.Lock ;
   try
      Canvas.Brush.Style := bsSolid ;
      Canvas.Brush.Color := Color ;
      Rect := Canvas.ClipRect ;
      Canvas.FillRect( Rect ) ;
   finally
      Canvas.Unlock ;
   end ;
end ;

initialization
   // Loads Custom Cursors
   Screen.Cursors[ crHandFlat ] := LoadCursor( HInstance, 'HANDFLAT' ) ;
   Screen.Cursors[ crHandGrab ] := LoadCursor( HInstance, 'HANDGRAB' ) ;
   Screen.Cursors[ crHandPnt ] := LoadCursor( HInstance, 'SG_HANDPNT' ) ;
   Screen.Cursors[ crXHair1 ] := LoadCursor( HInstance, 'SG_XHAIR1' ) ;
   Screen.Cursors[ crXHair2 ] := LoadCursor( HInstance, 'SG_XHAIR2' ) ;

end.

